#include "Cpci7841.h"
#include "Isrdpc.h"


extern PCI_Info *g_PciInfo[MAX_PCI_CARDS];

typedef struct
{
	UCHAR   Int1Enable:1;
	UCHAR   Int1ActHigh:1;
	UCHAR   Int1Status:1;
	UCHAR   Int2Enable:1;
	UCHAR   Int2ActHigh:1;
	UCHAR   Int2Status:1;
	UCHAR   PciIntEnable:1;
	UCHAR   SoftIntrrupt:1;
} PLX_INTCSR;

typedef struct
{
	UCHAR   ClockToggle:1;
	UCHAR   ChipSelect:1;
	UCHAR   DataIn:1;
	UCHAR   DataOut:1;
	UCHAR   Valid:1;
	UCHAR   ReloadConfig:1;
	UCHAR   SoftReset:1;
	UCHAR   MaskRevision:1;
} PLX_EEPROM;

typedef union _plx90_bytereg_
{
	PLX_EEPROM  eeprom;
	PLX_INTCSR  intcsr;
	UCHAR       r;
} PLX90_BYTEREG;

static U16 W7841IsrCheckSource(U32 dwPort)
{
	PLX90_BYTEREG rPlx;
	USHORT  wRetCode = 0;
	
	rPlx.r = Inbyte(dwPort + ADL_OP_LINTCSR);
	
	//int1
	if (rPlx.intcsr.SoftIntrrupt)
	{
		wRetCode |= 0x0200;
		/* must clear soft interrupt before return */
		rPlx.intcsr.SoftIntrrupt = 0;
		Outbyte(rPlx.r,dwPort + ADL_OP_LINTCSR);
	}
	
	if (rPlx.intcsr.PciIntEnable)
	{
		wRetCode |= 0x0100;	
	}
	
	if ( rPlx.intcsr.Int1Enable && rPlx.intcsr.Int1Status )
	{
		wRetCode |= 0x0001;	
	}
	
	if ( rPlx.intcsr.Int2Enable && rPlx.intcsr.Int2Status )
	{
		wRetCode |= 0x0002;	
	}
	
	return wRetCode;
}

static void W7841ReceiveMsg(PCI_Info* pDevExt, int nport)
{
	int i;
	U32 BaseAddr;
	unsigned char rPtrU;
	UCHAR data,dataStart;

	BaseAddr = pDevExt->address1 + 128 * nport;
	
	data = Inbyte(BaseAddr + 16);
	pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].len = data&0x0f;
	pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].rtr = (data&0x40) >> 6;
	
	if(data & 0x80)
	{
		dataStart = 21;
		
		data = Inbyte(BaseAddr + 17);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId  = data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId <<= 8;
		
		data = Inbyte(BaseAddr + 18);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId |= data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId <<= 8;
		
		data = Inbyte(BaseAddr + 19);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId  |= (USHORT)data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId <<= 8;
		
		data = Inbyte(BaseAddr + 20);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId  |= (USHORT)data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId >>= 3;
	}
	else
	{
		dataStart = 19;
		
		data = Inbyte(BaseAddr + 17);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId  = (USHORT)data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId <<= 8;
		
		data = Inbyte(BaseAddr + 18);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId |= (USHORT)data;
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].canId >>= 5;
	}
	
	for(i = 0; i < pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].len; i++)
	{
		data = Inbyte(BaseAddr + dataStart + i);
		pDevExt->rBuf[nport][pDevExt->rPtrU[nport]].data[i] = data;
	}

	// release receive buffer
	Outbyte(0x04, BaseAddr + 1);
	
	// Check if receive buffer is full
	rPtrU = pDevExt->rPtrU[nport] + 1;
	if(rPtrU == FIFOSIZE)
	{
		rPtrU = 0;	
	}
	
	if(rPtrU == pDevExt->rPtrL[nport])
	{
		return;
	}
			
	pDevExt->rPtrU[nport] = rPtrU;
				
/*	if(!nport)
	{
		if(pDevExt->reference > 0)
		adl_kill_fasync ( pDevExt->async_queue1, SIGEVENT1);
	} 
	else
	{
		if(pDevExt->reference > 0)
		adl_kill_fasync ( pDevExt->async_queue2, SIGEVENT2);
	}*/
}

static void W7841IsrProgram(PCI_Info* pDevExt, int nport)
{
	U32 BaseAddr;
	UCHAR flag, status;

	BaseAddr = pDevExt->address1 + 128 * nport;

	while((flag = Inbyte(BaseAddr + 3)) > 0)
	{
		status = Inbyte(BaseAddr + 2);
			
		// Wake-up interrupt
		if(flag & 0x10)
		{
			//printk(KERN_NOTICE "wake-up interrupt");
		}
		
		// Receive interrupt
		if(flag & 0x01)
		{
			while(status & 0x01)
			{
				W7841ReceiveMsg(pDevExt, nport);
				status = Inbyte(BaseAddr+2);
			}
		}
		
		// Data overrun interrupt
		if(flag & 0x08)
		{
			// Clear data overrun
			Outbyte(0x08, BaseAddr+2);
		}
		
		// Error warning interrupt
		if(flag & 0x04)
		{
			if(status & 0x80)
			{
				//printk(KERN_NOTICE "Bus off\n");
			}
			else if(status & 0x40)
			{
				//printk(KERN_NOTICE "Error warning\n");
			}
			else
			{
				//printk(KERN_NOTICE "Error active\n");
			}
		}
		
		// Bus error interrupt
		if(flag & 0x80)
		{
			//printk(KERN_NOTICE "Bus error interrupt\n");
		}
		
		// Error passive interrupt
		if(flag & 0x20)
		{
			//printk(KERN_NOTICE "Error passive interrupt\n");
		}
		// Arbitration lost interrupt
		if(flag & 0x40)
		{
			//printk(KERN_NOTICE "Arbitration lost interrupt\n");
		}
	}
}

irqreturn_t Cpci7841InterruptHandler(int boardNo)
{
	U32 dwPort;
	U16 wRetCode;
	PCI_Info* ppci_info;

	ppci_info = g_PciInfo[boardNo];
	dwPort = ppci_info->address0;
	wRetCode = W7841IsrCheckSource(dwPort);
	
//	logMsg("Cpci7841InterruptHandler!!!!!!!\n",0,0,0,0,0,0);

	if ((!(wRetCode & 0xff00)) ||(!(wRetCode & 0x0003))) //hw sw interrupt
	{
		return	IRQ_NONE;	
	}
		
	if (!ppci_info->initFlag)
	{
		//clear IRQ
		Inbyte(ppci_info->address1+3);
		Inbyte(ppci_info->address1+3+128);
		
		return IRQ_HANDLED;
	}

	if (wRetCode & 0x0001)  //int1
	{
		W7841IsrProgram(ppci_info ,0);	
		
		return IRQ_HANDLED;
	}
	if (wRetCode & 0x0002) //int2
	{

		W7841IsrProgram(ppci_info , 1);	
		
		return IRQ_HANDLED;
	}
	
	return IRQ_HANDLED;
}

#if 0
void adl_kill_fasync(struct fasync_struct *fa, int sig)
{
	struct fown_struct * fown;
	struct fasync_struct *temp_fa;
	int i;

	i = 0;
	temp_fa = fa;
	while (temp_fa)
	{
		fown = &temp_fa->fa_file->f_owner;
		if (fown->pid)
		{
			fown->signum = sig; // replace the fctnl( SIGSET ... )
		}
		temp_fa = temp_fa->fa_next;
	}
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
	kill_fasync( &fa, sig, POLL_IN );
#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	kill_fasync( fa, sig );
#endif

}
#endif
